package org.luckyjourney.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.luckyjourney.constant.RedisConstant;
import org.luckyjourney.service.FeedService;
import org.luckyjourney.util.DateUtil;
import org.luckyjourney.util.RedisCacheUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.DefaultTypedTuple;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.util.*;

/**
 * @description:
 * @Author: Xhy
 * @CreateTime: 2023-11-03 16:30
 */
@Service
public class FeedServiceImpl implements FeedService {



    @Autowired
    private RedisCacheUtil redisCacheUtil;

    @Autowired
    private RedisTemplate redisTemplate;



    @Override
    @Async
    public void pusOutBoxFeed(Long userId, Long videoId, Long time) {
        redisCacheUtil.zadd(RedisConstant.OUT_FOLLOW + userId, time, videoId, -1);
    }

    @Override
    public void pushInBoxFeed(Long userId, Long videoId, Long time) {
        // 需要推吗这个场景？只需要拉
    }

    @Override
    @Async
    public void deleteOutBoxFeed(Long userId,Collection<Long> fans,Long videoId) {
        String t = RedisConstant.IN_FOLLOW;
        redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
            for (Long fan : fans) {
                connection.zRem((t+fan).getBytes(),String.valueOf(videoId).getBytes());
            }
            connection.zRem((RedisConstant.OUT_FOLLOW + userId).getBytes(), String.valueOf(videoId).getBytes());
            return null;
        });
    }

    @Override
    @Async
    public void deleteInBoxFeed(Long userId,List<Long> videoIds) {
        redisTemplate.opsForZSet().remove(RedisConstant.IN_FOLLOW + userId, videoIds.toArray());
    }


    /**
     * 更新收件箱
     * @param  * 参数 userId
     * 参数 followIds
     * @return
    */
    @Override
    @Async
    public void updateFollowFeed(Long userId, Collection<Long> followIds) {
        String inKey = RedisConstant.IN_FOLLOW;
        final Date curDate = new Date();
        final Date limitDate = DateUtil.addDateDays(curDate, -7);
        final Set<ZSetOperations.TypedTuple<Long>> videoIds = redisTemplate.opsForZSet().reverseRangeWithScores(inKey + userId, 0, -1);
        // 主动拉取发件箱中最新一条视频至当前时间之内的关注的人发件箱的视频。
        if (!ObjectUtils.isEmpty(videoIds)) {
            Double oldTime = videoIds.iterator().next().getScore();
            update(userId,oldTime.longValue(),new Date().getTime(),followIds);
        } else {
            // 如果发件箱无数据，则拉取关注人发件箱中的 7 天之内的视频(redis的zset api 支持)。
            update(userId,limitDate.getTime(),curDate.getTime(),followIds);
        }
    }

    public void update(Long userId, Long min, Long max, Collection<Long> followIds) {
        String outKey = RedisConstant.OUT_FOLLOW;
        String inKey = RedisConstant.IN_FOLLOW;
        // 查看关注人的发件箱
        final List<Set<DefaultTypedTuple>> result = redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
                for (Long followId : followIds) {
                    connection.zRevRangeByScoreWithScores((outKey + followId).getBytes(), min, max, 0, 50);
                }
                return null;
        });
        final ObjectMapper objectMapper = new ObjectMapper();
        final HashSet<Long> ids = new HashSet<>();
        // 放入收件箱
        redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
            for (Set<DefaultTypedTuple> tuples : result) {
                if (!ObjectUtils.isEmpty(tuples)) {
                    for (DefaultTypedTuple tuple : tuples) {
                        final Object videoId = tuple.getValue();
                        ids.add(Long.parseLong(videoId.toString()));
                        final byte[] key = (inKey + userId).getBytes();
                        try {
                            connection.zAdd(key, tuple.getScore(), objectMapper.writeValueAsBytes(videoId));
                        } catch (JsonProcessingException e) {
                            e.printStackTrace();
                        }
                        connection.expire(key, RedisConstant.HISTORY_TIME);
                    }
                }
            }
            return null;
        });
    }

}
